using System;
using GeneralDFS.BusinessLogical.Nodes;
using GeneralDFS.Common;
using GeneralDFS.DataEntity.Nodes;

namespace GeneralDFS.BusinessLogical.RemoteObject
{
    public partial class NameNodeRemoteObject
    {
        /// <summary>
        /// Find the node that is the rightful owner of a given id.
        /// </summary>
        /// <param name="id">The id value whose successor should be found.</param>
        /// <returns>The ChordNode that is the Successor of the given ID value.</returns>
        public NameNodeInfo FindSuccessor(UInt64 id)
        {
            int hopCountOut = 0;    // ignore hopCount
            var nameNodeInfo = FindSuccessor(id, 0, out hopCountOut);
            //LogUtil.LogInfo("hopCountOut:{0}",hopCountOut);
            return nameNodeInfo;
        }

        /// <summary>
        /// Find the node that is the rightful owner of a given id.
        /// </summary>
        /// <param name="id">The id whose successor should be found.</param>
        /// <param name="hopCountIn"></param>
        /// <param name="hopCountOut"></param>
        /// <returns>The ChordNode that is the Successor of a given ID value.</returns>
        public NameNodeInfo FindSuccessor(UInt64 id, int hopCountIn, out int hopCountOut)
        {
            // is the local node's successor the rightful owner?
            if (DFSUtils.IsIDInRange(id, this.ID, this.Successor.ID))
            {
                hopCountOut = hopCountIn;
                return this.Successor;
            }
            else
            {
                // otherwise, find the nearest preceding finger, and ask that node.
                var predNode = FindClosestPrecedingFinger(id);
                return NameNode.CallFindSuccessor(predNode, id, 0, ++hopCountIn, out hopCountOut);
            }
        }

        /// <summary>
        /// Returns the closest successor preceding id.
        /// </summary>
        /// <param name="id">The id for which the closest finger should be found</param>
        /// <returns>The successor node of the closest finger to id in the current node's finger table</returns>
        private NameNodeInfo FindClosestPrecedingFinger(UInt64 id)
        {
            // iterate downward through the finger table looking for the right finger in the right range. if the finger is 
            // in the range but not valid, keep moving. if the entire finger table is checked without success, check the successor 
            // cache - if that fails, return the local node as the closest preceding finger.
            for (int i = this.FingerTable.Length - 1; i >= 0; i--)
            {
                // if the finger is more closely between the local node and id and that finger corresponds to a valid node, return the finger
                if (this.FingerTable.Successors[i] != null && this.FingerTable.Successors[i] != _nameNode.LocalNode)
                {
                    if (DFSUtils.FingerInRange(this.FingerTable.Successors[i].ID, this.ID, id))
                    {
                        var instance = NameNode.GetInstance(this.FingerTable.Successors[i]);
                        if (NameNode.IsInstanceValid(instance))
                        {
                            return this.FingerTable.Successors[i];
                        }
                    }
                }
            }

            // at this point, not even the successor is any good so go through the successor cache and run the same test
            for (int i = 0; i < this.SuccessorCache.Length; i++)
            {
                if (this.SuccessorCache[i] != null && this.SuccessorCache[i] != _nameNode.LocalNode)
                {
                    if (DFSUtils.FingerInRange(this.SuccessorCache[i].ID, this.ID, id))
                    {
                        var instance = NameNode.GetInstance(this.SuccessorCache[i]);
                        if (NameNode.IsInstanceValid(instance))
                        {
                            return this.SuccessorCache[i];
                        }
                    }
                }
            }

            // otherwise, if there is nothing closer, the local node is the closest preceding finger
            return _nameNode.LocalNode;
        }
    }
}